//	Server.cpp

#include "stdafx.h"
#include "Server.h"
#include "process.h"
#include "conio.h"
#include <fstream>
#include <sstream>

#pragma warning(disable:4996)

static void Thread1(void* p_this)
{
	Server* pxS = (Server*)p_this;
	pxS->CheckInput();
}

static void Thread2(void* p_this)
{
	Server* pxS = (Server*)p_this;
	pxS->CheckNewConnections();
}

static void Thread3(void* p_this)
{
	Server* pxS = (Server*)p_this;
	pxS->SendThread();
}

Server::Server()
{
	bQuit = false;
	m_iThreadsRunning = 0;
	m_iNumSendJobs = 0;
}

Server::~Server()
{

}

bool Server::Init()
{
	WinSock::Init();
	printf("myServer started..\n");

	IPAddr xLocal = IPAddr::GetLocalDefault();
	xLocal.SetPort(PORT);
	xLocal.SetHost();

	if (!m_xAcceptSocket.Open(xLocal, 0))
	{
		printf("Couldn't open accept socket\n");
		return false;
	}
	else
	{

		char* sIP = new char[64];
		xLocal.ToText(sIP, 64);
		printf("Accept socket open at: %s\n", sIP);
		return true;
	}
}

void Server::Run()
{
	_beginthread(Thread1, 0, this);
	_beginthread(Thread2, 0, this);
	_beginthread(Thread3, 0, this);

	while (m_iThreadsRunning == 0)
	{
		::Sleep(10);
	}
	while (m_iThreadsRunning > 0)
	{
		::Sleep(10);
	}
}

void Server::Close()
{
	WinSock::Shut();
}

void Server::HandleRequest(Request& p_xRequest, void* p_xData, int p_iSize)
{
	char*  rawText = (char*)p_xData;

	int iter = 0;
	int lineCount = 0;
	char** lineArray = new char*[MAX_REQ_LINES];
	for (int i = 0; i < p_iSize; i++)
	{
		if ((rawText[i] == '\r') && (rawText[i+1] == '\n'))
		{
			//we got a new line
			int lineSize = i - iter;
			lineArray[lineCount] = new char[MAX_REQ_CHARS_PER_LINE];
			for (int j = 0; j < lineSize; j++)
			{
				lineArray[lineCount][j] = rawText[iter+j];
			}

			//remove the null characters
			int iL=strlen(lineArray[lineCount]);
			while((iL>0)&&(lineArray[lineCount][iL-1]<=NULL)){iL--;};
			lineArray[lineCount][iL]='\0';

			lineCount++;
			iter = i+2;
		}
	}

	lineCount--;
	//insert the data in the Request struct

	//HEADER
	int tempCount;
	char** Line1 = xString::Split(' ', lineArray[0], tempCount);
	p_xRequest.mVerb = Line1[0];
	p_xRequest.mPath = Line1[1];
	p_xRequest.mVersion = Line1[2];

	//FIELDS
	for (int i = 1; i < lineCount; i++)
	{
		int numWords;
		char** Line = xString::Split(' ', lineArray[i], numWords);
		if (strcmp(const_cast<const char*>(Line[0]),"Accept:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mAccept += Line[j];
			}
		}
		else if (strcmp(Line[0],"Accept-Language:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mAcceptLa += Line[j];
			}
		}
		else if (strcmp(Line[0],"Accept-Encoding:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mAcceptEncoding += Line[j];
			}
		}
		else if (strcmp(Line[0],"User-Agent:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mUserAgent += Line[j];
			}
		}
		else if (strcmp(Line[0],"Connection:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mConnection += Line[j];
			}
		}
		else if (strcmp(Line[0],"Host:")==0)
		{
			for (int j = 1; j < numWords; j++)
			{
				p_xRequest.mHost += Line[j];
			}
		}
	}

}

void Server::CheckInput()
{
	::InterlockedIncrement((volatile LONG*)&m_iThreadsRunning);
	printf("Check input thread started\n");
	while (!bQuit)
	{
		::Sleep(10);
		if (kbhit())
		{
			printf(">");
			char sLine[256];
			gets(sLine);

			int iL=strlen(sLine);
			while((iL>0)&&(sLine[iL-1]<=' ')){iL--;};
			sLine[iL]='\0';
			if(strcmp(sLine,"quit")==0)
			{
				bQuit=true;
			}
			else
			{
				printf("%s\n", sLine);
				printf("No such commando\n");
			}
			
		}
	}
	printf("Check input thread stopped\n");
	::InterlockedDecrement((volatile LONG*)&m_iThreadsRunning);
}

void Server::CheckNewConnections()
{
	::InterlockedIncrement((volatile LONG*)&m_iThreadsRunning);
	printf("Check new connection thread started\n");
	
	while (!bQuit)
	{
		::Sleep(10);
		if (m_xAcceptSocket.ConnectionPending(100))
		{
 			TcpSocket *xNewCon = new TcpSocket();
			if (m_xAcceptSocket.Accept(*xNewCon))
			{
				m_xLock.Lock();
				printf("We have a new connection!\n");
				m_xaConnections.push_back(xNewCon);
				m_xLock.Unlock();
			}
		}
		else
		{
			WSAResetEvent(m_xAcceptSocket.m_hConReqEvt);
		};
	}
	
	printf("Check new connection thread stopped\n");
	::InterlockedDecrement((volatile LONG*)&m_iThreadsRunning);
}

namespace
{
	enum
	{
		DONE,
		INPROGRESS
	};

	void SendData(void* p_xData, TcpSocket* p_xSock)
	{
		SendJob* myJob = (SendJob*)p_xData;

		myJob->status = INPROGRESS;

		std::string sNewMessage = "";
		std::string sDirectory = "../www";
		xFile myFile;

		if (myJob->p_cRequest.mPath == "/")
		{
			sDirectory += "/index.html";
		}
		else
		{
			sDirectory += myJob->p_cRequest.mPath;
		}

		if (myFile.Exists(sDirectory.c_str()))
		{
			//compose the message
			sNewMessage += myJob->p_cRequest.mVersion;
			sNewMessage += " 200 OK\r\n";

			sNewMessage += "Date: "; 
			time_t rawtime;
			struct tm * timeinfo;
			time(&rawtime);
			timeinfo = localtime(&rawtime);
			sNewMessage += asctime(timeinfo);

			timeinfo->tm_hour += 1;
			sNewMessage += "Expires: ";
			sNewMessage += asctime(timeinfo);
			
			char* cMesssageBody;
			if (xString::getExtension(myJob->p_cRequest.mPath) == "jpg")
			{
				myFile.Open(sDirectory.c_str(), 0);
				cMesssageBody = myFile.ReadFile();
				myFile.Close();
				sNewMessage += "Content-Type: image/jpeg\r\n";
			}
			if (xString::getExtension(myJob->p_cRequest.mPath) == "gif")
			{
				myFile.Open(sDirectory.c_str(), 0);
				cMesssageBody = myFile.ReadFile();
				myFile.Close();
				sNewMessage += "Content-Type: image/gif\r\n";
			}
			else if ((xString::getExtension(myJob->p_cRequest.mPath) == "html") || (myJob->p_cRequest.mPath == "/"))
			{
				myFile.Open(sDirectory.c_str(), 1);
				cMesssageBody = myFile.ReadFile();
				myFile.Close();
				sNewMessage += "Content-Type: text/html; charset=UTF-8\r\n";
			}

			if (myJob->p_cRequest.mVersion == "HTTP/1.0")
			{
				sNewMessage += "Connection: Keep-Alive\r\n";
			}

			char temp[30];
			sprintf(temp, "Content-Length: %d", myFile.GetSize());
			sNewMessage += temp;

			sNewMessage += "\r\n\r\n";

			//End of Message

			if (myJob->p_cRequest.mVerb == "HEAD" || myJob->p_cRequest.mVerb == "GET")
			{
				int number_sent = p_xSock->Send(const_cast<char*>(sNewMessage.c_str()), (int)sNewMessage.length());
				printf("Number of bytes sent: %d\n", number_sent);
			}
			if (myJob->p_cRequest.mVerb == "GET")
			{
				int number_sent = p_xSock->Send(cMesssageBody, myFile.GetSize());
				IPAddr me = p_xSock->GetRemoteAddr();
				printf("Number of bytes sent: %d\n", number_sent);
			}
			else if(myJob->p_cRequest.mVerb != "HEAD" || myJob->p_cRequest.mVerb != "GET")
			{
				sNewMessage = "";
				sNewMessage += myJob->p_cRequest.mVersion;
				sNewMessage += " 405 Method Not Allowed\r\n";
				sNewMessage += "Allow: GET, HEAD\r\n";
				sNewMessage += "Connection: close\r\n";
				sNewMessage += "Content-Length: 0\r\n\r\n";

				int number_sent = p_xSock->Send(const_cast<char*>(sNewMessage.c_str()), (int)sNewMessage.length());
				printf("Number of bytes sent: %d\n", number_sent);

			}
				
		}
		else
		{
			//File not found
			sNewMessage += myJob->p_cRequest.mVersion;
			sNewMessage += " 404 File not found\r\n";
			sNewMessage += "Content-Length: 0\r\n\r\n";

			int succed = p_xSock->Send(const_cast<char*>(sNewMessage.c_str()), sNewMessage.length());
				
		}
		myJob->status = DONE;
	};
};

void Server::SendThread()
{
	::InterlockedIncrement((volatile LONG*)&m_iThreadsRunning);
	printf("Send data thread started\n");

	while (!bQuit)
	{
		::Sleep(10);
		m_xLock.Lock();
		std::vector<TcpSocket*>::iterator connection_iter = m_xaConnections.begin();
		while (connection_iter != m_xaConnections.end())
		{
			if ((*connection_iter)->IsConnected())
			{
				int iSize = (*connection_iter)->PendingRecv();

				if (iSize > 0)
				{
					char* data = new char[iSize];
					(*connection_iter)->Recv(data, iSize);
					if (CheckRecvEnd(data))
					{
						Request rD;
						HandleRequest(rD, data, iSize);
						SendData(&rD, (*connection_iter));
						(*connection_iter)->m_iLastActionTime = ::GetTickCount();
					}

				}
				int iTick = ::GetTickCount();
				if (iTick-((*connection_iter)->m_iLastActionTime) > CONNECTION_TIMEOUT_SEC*1000)
				{
					(*connection_iter)->Close();
				}
				connection_iter++;
			}
		}

		
		for (int i = 0; i < m_xaConnections.size(); i++)
		{
			if (!m_xaConnections.at(i)->IsConnected())
			{
				connection_iter = m_xaConnections.begin();
				for (int j = 0; j < i; j++)
				{
					connection_iter++;
				}
				m_xaConnections.erase(connection_iter);
			}
		}
		m_xLock.Unlock();
	}

	printf("Send data thread stopped\n");
	::InterlockedDecrement((volatile LONG*)&m_iThreadsRunning);
}

bool Server::CheckRecvEnd(char* data)
{
	int size = strlen(data);

	int iL=strlen(data);
	while((iL>0)&&(data[iL-1]<=NULL)){iL--;};
	data[iL]='\0';

	size = strlen(data);

	if (size < 4)
	{
		return false;
	}
	char end[4];
	for (int i = 0; i < 4; i++)
	{
		end[i] = data[size-(i+1)];
	}

	iL=strlen(end);
	while((iL>0)&&(end[iL-1]<=0)){iL--;};
	end[iL]='\0';

	if (end[0] == 10 && end[1] == 13 && end[2] == 10 && end[3] == 13)
	{
		return true;
	}
	else
	{
		return false;
	}
}